package com.zhoust.fastdome.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhoust.fastdome.annnotation.LogAutoRecord;
import com.zhoust.fastdome.business.dao.CommonLogDao;
import com.zhoust.fastdome.business.entity.CommonLog;
import com.zhoust.fastdome.business.service.CommonLogService;
import com.zhoust.fastdome.common.CommonConstant;
import com.zhoust.fastdome.common.CommonRequestBody;
import com.zhoust.fastdome.utils.IPUtils;
import com.zhoust.fastdome.utils.MyRequestUtils;
import com.zhoust.fastdome.utils.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * (CommonLog)表服务实现类
 *
 * @author makejava
 * @since 2021-09-22 17:37:53
 */
@Service("commonLogService")
@Slf4j
public class CommonLogServiceImpl extends ServiceImpl<CommonLogDao, CommonLog> implements CommonLogService {

    @Value("${fast.dome.logType}")
    private String logType;

    final HttpServletRequest request;

    public CommonLogServiceImpl(HttpServletRequest request) {
        this.request = request;
    }

    /**
     * 获取参数插入日志表,异步执行
     * @param responseBody
     * @param joinPoint
     */
    @Override
    public void saveLog(String responseBody, ProceedingJoinPoint joinPoint){
        log.info("AOP异步记录日志");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        LogAutoRecord annotation = method.getAnnotation(LogAutoRecord.class);
        String methodDesc = "通过包扫描记录日志";
        if(null != annotation){
            methodDesc = annotation.methodDesc();
        }
        String requestBody = getRequestBody(request,joinPoint);
        CommonLog commonLog = getCommonLog(request,requestBody);
        commonLog.setMethodName(methodName);
        commonLog.setResponseBody(responseBody);
        commonLog.setUriDesc(methodDesc);
        // 建议使用异步线程记录，避免堵塞
        super.save(commonLog);
    }

    /**
     * 通过前置处理器获取参数插入日志表,异步执行
     */
    @Override
    public void saveLogByHandler(HttpServletRequest request){
        log.info("拦截器异步记录日志");
        String methodDesc = "通过拦截器写入日志";
        String methodName = request.getMethod();
        String requestBody = getRequestBody(request, null);
        CommonLog commonLog = getCommonLog(request,requestBody);
        commonLog.setMethodName(methodName);
        commonLog.setResponseBody("");
        commonLog.setUriDesc(methodDesc);
        // 从请求中获取（建议使用统一的请求流水，从前台传入，可以做重复请求校验）
        commonLog.setLogId(String.valueOf(request.getAttribute("logId")));
        // 建议使用异步线程记录，避免堵塞
        super.save(commonLog);
    }
    /**
     * 通过后置处理器更新日志返回报文,异步执行
     */
    @Override
    public void updateLogByHandler(HttpServletRequest request,String responseBody){
        log.info("拦截器异步更新日志");
        CommonLog commonLog = new CommonLog();
        commonLog.setLogId(String.valueOf(request.getAttribute("logId")));
        commonLog.setResponseBody(responseBody);
        commonLog.setMethodName(String.valueOf(request.getAttribute("methodName")));
        // 建议使用异步线程记录，避免堵塞
        super.updateById(commonLog);
    }


    /**
     * 新项目建议定义好DTO请求参数统一使用一个继承一个公共的对象,
     *  我这里定义了一个 CommonRequestBody,用来存公共的流水，及系统来源。
     *  在别的项目中 参数可能是在parameter中，也可能在请求的请求体中。
     *  根据实际项目去判断
     * @param request
     * @return
     */
    @Override
    public String getRequestBody(HttpServletRequest request, ProceedingJoinPoint joinPoint) {
        String requestBody = "";
        try {
            if(CommonConstant.aopAnnotation.equals(logType)||CommonConstant.aopPackage.equals(logType)){
                // AOP 方式
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                // 只获取接口用到的参数，没用到的不获取
                String[] parameterNames = signature.getParameterNames();
                Object[] paramsArray = joinPoint.getArgs();
                 if (paramsArray != null && paramsArray.length > 0){
                    for (int i = 0; i < paramsArray.length; i++){
                        if (!StringUtils.isEmpty(paramsArray[i])){
                            Object jsonObj = JSONObject.toJSONString(paramsArray[i]);
                            requestBody += "\""+parameterNames[i]  + "\":" + jsonObj.toString();
                        }
                    }
                }
                requestBody.trim();

            }
            if(CommonConstant.adapter.equals(logType)){
                // 拦截器获取请求方式
                MyRequestUtils myRequestUtils = (MyRequestUtils)request;
                requestBody = myRequestUtils.getBody();

                // 如果body 中没有，就去parameter中取
                if(StringUtils.isEmpty(requestBody)){
                    Map<String, String[]> parameterMap = request.getParameterMap();
                    requestBody = JSONObject.toJSONString(parameterMap);
                }
            }
        }catch (Exception e){
            log.info("获取请求报文失败，{}",e);
        }
        return requestBody;
    }


    @Override
    public CommonLog getCommonLog(HttpServletRequest request,String requestBody){
        CommonLog commonLog = new CommonLog();
        String logId  = "";
        Object logId1 = request.getAttribute("logId");
        if(StringUtils.isEmpty(logId1)){
            logId = String.valueOf(SnowflakeIdWorker.generateId());
        }else{
            logId = (String) logId;
        }
        // 获取 工号信息 (一般在session获取，根据自己业务)
        String operaType = "common";
        String operaName = "测试";
        String operaNo = "6666";
        String requestIp = IPUtils.getIpAddr(request);
        String requestURI = request.getRequestURI();
        commonLog.setLogId(logId);
        commonLog.setOperaType(operaType);
        commonLog.setOperaNam(operaName);
        commonLog.setOperaNo(operaNo);
        commonLog.setRemoteIp(requestIp);
        commonLog.setRequestUri(requestURI);
        commonLog.setRequestBody(requestBody);
        commonLog.setCreateTime(new Date());
        return commonLog;
    }
}

